/**
 * Copyright 2017 orivil Authors. All rights reserved.
 * Use of this source code is governed by a MIT-style
 * license that can be found at https://mit-license.org.
 */

package swagger

import (
	"reflect"
	"gitee.com/tugoer/param"
)

func InText(in param.Where) string {
	switch in {
	case param.Query:
		return "query"
	case param.Form:
		//return "query" // for adapting OpenApi3.0
		return "formData" // for adapting OpenApi2.0
	case param.Path:
		return "path"
	case param.Header:
		return "header"
	case param.Cookie:
		return "cookie" // only support OpenApi3.0
	default:
		return "invalid"
	}
}

// operation parameters
type Parameters []*Parameter

type Parameter struct {
	// Refer to a defined parameter.
	// This field is not swagger Parameter Object filed, because parameters could be Parameter Objects
	// or Reference Objects, see: http://swagger.io/specification/#operation-object-36
	// If the Ref field was set, other fields should be empty, so the Parameter object could be treated
	// as a Reference Object.
	Ref string `json:"$ref,omitempty"`

	// Required.
	// The name of the parameter. Parameter names are case sensitive.
	// If in is "path", the name field MUST correspond to the associated path
	// segment from the path field in the Paths Object(http://swagger.io/specification/#pathsObject).
	// See Path Templating(http://swagger.io/specification/#pathTemplating) for further information.
	// For all other cases, the name corresponds to the
	// parameter name used based on the in property.
	Name string `json:"name,omitempty"`

	// Required.
	// The location of the parameter.
	// Possible values are "query", "header", "path", "formData" or "body".
	// "body" 在 OpenApi3.0 中被弃用了, 不建议使用, 同时在 OpenApi3.0 中增加了 "cookie"
	In string `json:"in,omitempty"`

	// A brief description of the parameter. This could contain examples of use.
	// GFM syntax(https://guides.github.com/features/mastering-markdown/#GitHub-flavored-markdown)
	// can be used for rich text representation.
	Description string `json:"description,omitempty"`

	// Determines whether this parameter is mandatory. If the parameter is in "path",
	// this property is required and its value MUST be true. Otherwise, the property MAY be included
	// and its default value is false.
	Required bool `json:"required,omitempty"`

	// If in is "body":
	Schema *Schema `json:"schema,omitempty"`

	// If in is any value other than "body":

	// Required.
	// The type of the parameter. Since the parameter is not located at the request body, it is limited to
	// simple types (that is, not an object). The value MUST be one of "string", "number", "integer",
	// "boolean", "array" or "file". If type is "file", the consumes MUST be either "multipart/form-data",
	// " application/x-www-form-urlencoded" or both and the parameter MUST be in "formData".
	Type string `json:"type,omitempty"`

	// The extending format for the previously mentioned type.
	// See Data Type Formats(http://swagger.io/specification/#dataTypeFormat) for further details.
	Format string `json:"format,omitempty"`

	// Sets the ability to pass empty-valued parameters. This is valid only for either query or formData
	// parameters and allows you to send a parameter with a name only or an empty value. Default value is false.
	AllowEmptyValue bool `json:"allowEmptyValue,omitempty"`

	// Required if type is "array". Describes the type of items in the array.
	Items *Items `json:"items,omitempty"`

	// Determines the format of the array if type array is used. Possible values are:
	//	csv - comma separated values foo,bar.
	//	ssv - space separated values foo bar.
	//	tsv - tab separated values foo\tbar.
	//	pipes - pipe separated values foo|bar.
	//	multi - corresponds to multiple parameter instances instead of multiple values for
	// 		a single instance foo=bar&foo=baz. This is valid only for parameters in
	// 		"query" or "formData".
	// Default value is csv.
	CollectionFormat string `json:"collectionFormat,omitempty"`

	// Declares the value of the parameter that the server will use if none is provided, for example
	// a "count" to control the number of results per page might default to 100 if not supplied by
	// the client in the request. (Note: "default" has no meaning for required parameters.)
	// See https://tools.ietf.org/html/draft-fge-json-schema-validation-00#section-6.2. Unlike JSON
	// Schema this value MUST conform to the defined type for this parameter.
	Default interface{} `json:"default,omitempty"`

	*Validation
}

func (p *Parameter) setDataType(typ, format string, items *Items) {
	p.Type = typ
	p.Format = format
	p.Items = items
}

func NewParameters(ps []*param.Parameter) Parameters {
	var parameters Parameters
	for _, p := range ps {
		st := param.ReflectTypeOf(p.Schema, param.NewFieldNameProvider(p.Ignore), p.Validator)
		st.Range(func(kind reflect.Kind, field reflect.StructField, cdt param.ConditionProvider) {
			sp := &Parameter{Name: field.Name, In: InText(p.In)}
			setDataType(kind, sp)
			tag := field.Tag
			format := tag.Get("format")
			if format != "" {
				sp.Format = format
			}
			typ := tag.Get("type")
			if typ != "" {
				sp.Type = typ
			}
			// if id := tag.Get("id"); id != "" {
			// 	sp.Description = "id:`" + id + "` "
			// }
			sp.Description += tag.Get("desc")
			// sp.Description += initParamTagMsg(tag)
			if cdt != nil {
				if cdt.Info().Required != nil {
					sp.Required = true
				}
			}
			parameters = append(parameters, sp)
		})
	}
	return parameters
}

func initParamTagMsg(tag reflect.StructTag) string {
	msg := ""
	var parseTag = func(key string) string {
		if v := tag.Get(key); v != "" {
			return v + "    "
		} else {
			return ""
		}
	}
	tags := []string{
		param.TagID,
		param.TagRequired,
		param.MsgName(param.TagLen),
		param.MsgName(param.TagNum),
		param.MsgName(param.TagItem),
		param.MsgName(param.TagFileKB),
		param.TagEmail,
		param.MsgName(param.TagEnum),
		param.MsgName(param.TagRegexp),
	}
	for _, t := range tags {
		m := parseTag(t)
		if m != "" {
			msg += t + `:"` + m + `"`
		}
	}
	return msg
}